﻿// http://stackoverflow.com/questions/808736/set-system-time-zone-from-net
using System;
using System.Runtime.InteropServices;
using Microsoft.Win32;
using System.Globalization;
using MefTZAddin.interop;
using MefTZAddin.interop.types;

namespace MefTZAddin
{
    public static class DynamicTimeZoneInterop
    {
        [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
        private static extern int GetDynamicTimeZoneInformation(out DynamicTimeZoneInformation lpTimeZoneInformation);

        [DllImport("kernel32.dll", CharSet = CharSet.Auto)]
        private static extern bool SetDynamicTimeZoneInformation([In] ref DynamicTimeZoneInformation lpTimeZoneInformation);
        
        public static void SetDynamicTimeZone(DynamicTimeZoneInformation dtzi)
        {
            // Setting timezone is filtered from token by default
            AdjustPrivileges.SetTokenPrivilege(AdjustPrivileges.SE_TIME_ZONE_NAME);
            Win32.CheckWin32Error();

            try
            {
                // Set local system timezone
                if (SetDynamicTimeZoneInformation(ref dtzi))
                {
                    Win32.CheckWin32Error();
                }
                else
                {
                    // TODO: Figure out how to update local settings since refreshing doesn't actually work...
                    SendMessageInterop.BroadcastSettingsChange();
                    CultureInfo.CurrentCulture.ClearCachedData();
                    TimeZoneInfo.ClearCachedData();
                }
            }
            finally
            {
                // Setting timezone is filtered from token by default
                AdjustPrivileges.UnsetTokenPrivilege(AdjustPrivileges.SE_TIME_ZONE_NAME);
                Win32.CheckWin32Error();
            }
        }

        public static void SetDynamicTimeZone(TimeZoneInfo tzi)
        {
            SetDynamicTimeZone(tzi.ToDynamicTimeZoneInformation());
        }

        public static DynamicTimeZoneInformation GetDynamicTimeZone()
        {
            DynamicTimeZoneInformation dtzi;

            int currentTimeZone = GetDynamicTimeZoneInformation(out dtzi);

            Win32.CheckWin32Error();

            return dtzi;
        }

        public static DynamicTimeZoneInformation ToDynamicTimeZoneInformation(this TimeZoneInfo tzi)
        {
            DynamicTimeZoneInformation cpy = new DynamicTimeZoneInformation();
            cpy.bias = (int)tzi.BaseUtcOffset.TotalMinutes;

            TimeZoneInfo.AdjustmentRule[] rules = tzi.GetAdjustmentRules();

            if (tzi.SupportsDaylightSavingTime && rules.Length > 0)
            {
                cpy.daylightName = tzi.DaylightName;
                cpy.daylightBias = (int)rules[0].DaylightDelta.TotalMinutes;
                cpy.dynamicDaylightTimeDisabled = false;

                foreach (var rule in rules)
                {
                    if (DateTime.Now >= rule.DateStart && DateTime.Now <= rule.DateEnd)
                    {
                        cpy.daylightDate = rule.DaylightTransitionStart.ToSystemTime();
                        cpy.standardDate = rule.DaylightTransitionEnd.ToSystemTime();
                        break;
                    }
                }
                cpy.daylightDate = rules[0].DateStart.ToSystemTime();
            }
            else
            {
                cpy.daylightName = "";
                cpy.dynamicDaylightTimeDisabled = true;
            }

            cpy.standardName = tzi.StandardName;
            cpy.timeZoneKeyName = tzi.StandardName;

            return cpy;
        }
    }
}
